<template>
  <div class="app-container">
    <div class="chat-page">
      <el-row :gutter="20" style="height: 100%;">
        <!-- 左侧设置面板 -->
        <el-col :span="6" style="height: 100%;">
          <el-card class="settings-panel">
            <div slot="header" class="settings-header">
              <span>模型设置</span>
            </div>

            <!-- 模型选择 -->
            <div class="setting-section">
              <div class="section-title">选择模型</div>
              <el-select v-model="selectedModel" placeholder="请选择模型" style="width: 100%">
                <el-option-group
                  v-for="(models, category) in modelsByCategory"
                  :key="category"
                  :label="category + ' 系列'"
                >
                  <el-option
                    v-for="model in models"
                    :key="model.id"
                    :label="model.name"
                    :value="model.id"
                  >
                    <span style="float: left">{{ model.name }}</span>
                    <span style="float: right; color: #8492a6; font-size: 13px">
                      {{ model.price === '免费' ? model.price : '¥' + model.price }}
                    </span>
                  </el-option>
                </el-option-group>
              </el-select>
            </div>

            <!-- 参数设置 -->
            <div class="setting-section">
              <div class="section-title">参数设置</div>

              <div class="param-item">
                <div class="param-label">
                  <span>Max Tokens</span>
                  <el-tooltip content="生成文本的最大长度" placement="top">
                    <i class="el-icon-question" />
                  </el-tooltip>
                </div>
                <el-slider v-model="params.maxTokens" :min="100" :max="4000" :step="100" show-stops />
                <div class="param-value">{{ params.maxTokens }}</div>
              </div>

              <div class="param-item">
                <div class="param-label">
                  <span>Temperature</span>
                  <el-tooltip content="控制生成文本的随机性" placement="top">
                    <i class="el-icon-question" />
                  </el-tooltip>
                </div>
                <el-slider v-model="params.temperature" :min="0" :max="1" :step="0.01" show-input />
              </div>

              <div class="param-item">
                <div class="param-label">
                  <span>Top-P</span>
                  <el-tooltip content="控制生成文本的多样性" placement="top">
                    <i class="el-icon-question" />
                  </el-tooltip>
                </div>
                <el-slider v-model="params.topP" :min="0" :max="1" :step="0.01" show-input />
              </div>

              <div class="param-item">
                <div class="param-label">
                  <span>Top-K</span>
                  <el-tooltip content="每一步只考虑概率最高的K个词" placement="top">
                    <i class="el-icon-question" />
                  </el-tooltip>
                </div>
                <el-slider v-model="params.topK" :min="1" :max="100" :step="1" show-input />
              </div>

              <div class="param-item">
                <div class="param-label">
                  <span>Frequency Penalty</span>
                  <el-tooltip content="降低重复词语的概率" placement="top">
                    <i class="el-icon-question" />
                  </el-tooltip>
                </div>
                <el-slider v-model="params.frequencyPenalty" :min="0" :max="2" :step="0.01" show-input />
              </div>

              <div v-if="isVVMModel" class="param-item">
                <div class="param-label">
                  <span>Thinking Budget</span>
                  <el-tooltip content="模型思考的预算" placement="top">
                    <i class="el-icon-question" />
                  </el-tooltip>
                </div>
                <el-slider v-model="params.thinkingBudget" :min="0" :max="1" :step="0.01" show-input />
              </div>
            </div>
          </el-card>
        </el-col>

        <!-- 右侧聊天区域 -->
        <el-col :span="18" style="height: 100%;">
          <el-card class="chat-container" :body-style="{height: '100%'}">
            <div class="chat-header">
              <div class="model-info">
                <span class="model-name">{{ getSelectedModelName() }}</span>
                <el-tag size="mini" :type="getModelPriceType()">{{ getModelPrice() }}</el-tag>
              </div>
              <el-button size="mini" type="text" @click="clearChat">
                <i class="el-icon-delete" /> 清空对话
              </el-button>
            </div>

            <div class="chat-body">
              <div ref="messagesBox" class="messages">
                <template v-if="messages.length === 0">
                  <div class="empty-chat">
                    <i class="el-icon-chat-dot-round" />
                    <p>开始一段新的对话吧</p>
                  </div>
                </template>
                <div
                  v-for="(msg, index) in messages"
                  :key="index"
                  class="message-item"
                  :class="msg.role"
                >
                  <div class="message-avatar">
                    <el-avatar :size="40" :icon="msg.role === 'user' ? 'el-icon-user' : 'el-icon-s-platform'" />
                  </div>
                  <div class="message-content">
                    <div class="message-role">{{ msg.role === 'user' ? '你' : 'AI' }}</div>
                    <div class="content" v-html="msg.content" />
                    <div v-if="msg.error" class="error-message">
                      <i class="el-icon-warning-outline" /> {{ msg.error }}
                    </div>
                  </div>
                </div>
              </div>
              <div class="input-area">
                <el-input
                  v-model="input"
                  type="textarea"
                  placeholder="请输入你的问题..."
                  :rows="3"
                  class="input-box"
                  @keyup.enter.native="handleEnterPress"
                  @keyup.ctrl.enter="sendPrompt"
                />

                <div class="buttons">
                  <el-tooltip content="按 Enter 发送，Shift+Enter 换行" placement="top">
                    <el-button type="primary" :disabled="isStreaming || !input.trim()" @click="sendPrompt">
                      <i class="el-icon-s-promotion" /> 发送
                    </el-button>
                  </el-tooltip>
                  <el-button type="danger" :disabled="!isStreaming" @click="stopStream">
                    <i class="el-icon-close" /> 停止
                  </el-button>
                </div>
              </div>
            </div>
          </el-card>
        </el-col>
      </el-row>
    </div>
  </div>
</template>

<script>
import { getToken } from '@/utils/auth'

export default {
  name: 'ChatDialog',
  data() {
    return {
      input: '',
      messages: [],
      isStreaming: false,
      source: null,
      lastEventData: null,
      selectedModel: 'deepseek-ai/DeepSeek-R1-0528-Qwen3-8B',
      params: {
        maxTokens: 1000,
        temperature: 0.7,
        topP: 0.9,
        topK: 50,
        frequencyPenalty: 0.5,
        thinkingBudget: 0.5
      },
      modelList: [
        // MiniMax 系列
        { id: 'MiniMaxAI/MiniMax-M1-80k', name: 'MiniMax-M1-80k', price: '8', category: 'MiniMax' },

        // Tongyi 系列
        { id: 'Tongyi-Zhiwen/QwenLong-L1-32B', name: 'QwenLong-L1-32B', price: '4', category: 'Tongyi' },

        // Qwen 系列
        { id: 'Qwen/Qwen3-30B-A3B', name: 'Qwen3-30B-A3B', price: '2.8', category: 'Qwen' },
        { id: 'Qwen/Qwen3-32B', name: 'Qwen3-32B', price: '4', category: 'Qwen' },
        { id: 'Qwen/Qwen3-14B', name: 'Qwen3-14B', price: '2', category: 'Qwen' },
        { id: 'Qwen/Qwen3-8B', name: 'Qwen3-8B', price: '免费', category: 'Qwen' },
        { id: 'Qwen/Qwen3-235B-A22B', name: 'Qwen3-235B-A22B', price: '10', category: 'Qwen' },
        { id: 'Qwen/QwQ-32B', name: 'QwQ-32B', price: '4', category: 'Qwen' },
        { id: 'Qwen/Qwen2.5-72B-Instruct-128K', name: 'Qwen2.5-72B-Instruct-128K', price: '4.5', category: 'Qwen' },
        { id: 'Qwen/Qwen2.5-72B-Instruct', name: 'Qwen2.5-72B-Instruct', price: '4.13', category: 'Qwen' },
        { id: 'Qwen/Qwen2.5-32B-Instruct', name: 'Qwen2.5-32B-Instruct', price: '1.89', category: 'Qwen' },
        { id: 'Qwen/Qwen2.5-14B-Instruct', name: 'Qwen2.5-14B-Instruct', price: '1', category: 'Qwen' },
        { id: 'Qwen/Qwen2.5-7B-Instruct', name: 'Qwen2.5-7B-Instruct', price: '0.5', category: 'Qwen' },
        { id: 'Qwen/Qwen2.5-Coder-32B-Instruct', name: 'Qwen2.5-Coder-32B-Instruct', price: '1.89', category: 'Qwen' },
        { id: 'Qwen/Qwen2.5-Coder-7B-Instruct', name: 'Qwen2.5-Coder-7B-Instruct', price: '0.5', category: 'Qwen' },
        { id: 'Qwen/Qwen2-7B-Instruct', name: 'Qwen2-7B-Instruct', price: '0.5', category: 'Qwen' },
        { id: 'Qwen/Qwen2-1.5B-Instruct', name: 'Qwen2-1.5B-Instruct', price: '0.2', category: 'Qwen' },
        { id: 'Qwen/QwQ-32B-Preview', name: 'QwQ-32B-Preview', price: '1.26', category: 'Qwen' },
        { id: 'Vendor-A/Qwen/Qwen2.5-72B-Instruct', name: 'Vendor-A/Qwen2.5-72B-Instruct', price: '4.13', category: 'Qwen' },

        // THUDM 系列
        { id: 'THUDM/GLM-Z1-32B-0414', name: 'GLM-Z1-32B', price: '3.8', category: 'THUDM' },
        { id: 'THUDM/GLM-4-32B-0414', name: 'GLM-4-32B', price: '3.8', category: 'THUDM' },
        { id: 'THUDM/GLM-Z1-Rumination-32B-0414', name: 'GLM-Z1-Rumination-32B', price: '4', category: 'THUDM' },
        { id: 'THUDM/GLM-4-9B-0414', name: 'GLM-4-9B', price: '1', category: 'THUDM' },
        { id: 'THUDM/glm-4-9b-chat', name: 'glm-4-9b-chat', price: '1', category: 'THUDM' },

        // DeepSeek 系列
        { id: 'deepseek-ai/DeepSeek-R1', name: 'DeepSeek-R1', price: '16', category: 'DeepSeek' },
        { id: 'deepseek-ai/DeepSeek-V3', name: 'DeepSeek-V3', price: '8', category: 'DeepSeek' },
        { id: 'deepseek-ai/DeepSeek-R1-0528-Qwen3-8B', name: 'DeepSeek-R1-0528-Qwen3-8B', price: '免费', category: 'DeepSeek' },
        { id: 'deepseek-ai/DeepSeek-R1-Distill-Qwen-32B', name: 'DeepSeek-R1-Distill-Qwen-32B', price: '1.26', category: 'DeepSeek' },
        { id: 'deepseek-ai/DeepSeek-R1-Distill-Qwen-14B', name: 'DeepSeek-R1-Distill-Qwen-14B', price: '0.7', category: 'DeepSeek' },
        { id: 'deepseek-ai/DeepSeek-R1-Distill-Qwen-7B', name: 'DeepSeek-R1-Distill-Qwen-7B', price: '免费', category: 'DeepSeek' },
        { id: 'deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B', name: 'DeepSeek-R1-Distill-Qwen-1.5B', price: '免费', category: 'DeepSeek' },
        { id: 'deepseek-ai/DeepSeek-V2.5', name: 'DeepSeek-V2.5', price: '1.33', category: 'DeepSeek' },

        // TeleAI 系列
        { id: 'TeleAI/TeleChat2', name: 'TeleChat2', price: '2', category: 'TeleAI' },

        // InternLM 系列
        { id: 'internlm/internlm2_5-7b-chat', name: 'internlm2_5-7b-chat', price: '0.5', category: 'InternLM' },
        { id: 'internlm/internlm2_5-20b-chat', name: 'internlm2_5-20b-chat', price: '1.5', category: 'InternLM' },

        // Pro 专业版模型
        { id: 'Pro/deepseek-ai/DeepSeek-R1', name: 'Pro/DeepSeek-R1', price: '8', category: 'Pro' },
        { id: 'Pro/deepseek-ai/DeepSeek-R1-0120', name: 'Pro/DeepSeek-R1-0120', price: '8', category: 'Pro' },
        { id: 'Pro/deepseek-ai/DeepSeek-V3', name: 'Pro/DeepSeek-V3', price: '4', category: 'Pro' },
        { id: 'Pro/deepseek-ai/DeepSeek-R1-Distill-Qwen-7B', name: 'Pro/DeepSeek-R1-Distill-Qwen-7B', price: '0.35', category: 'Pro' },
        { id: 'Pro/deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B', name: 'Pro/DeepSeek-R1-Distill-Qwen-1.5B', price: '0.1', category: 'Pro' },
        { id: 'Pro/Qwen/Qwen2.5-7B-Instruct', name: 'Pro/Qwen2.5-7B-Instruct', price: '0.35', category: 'Pro' },
        { id: 'Pro/Qwen/Qwen2-7B-Instruct', name: 'Pro/Qwen2-7B-Instruct', price: '0.35', category: 'Pro' },
        { id: 'Pro/Qwen/Qwen2-1.5B-Instruct', name: 'Pro/Qwen2-1.5B-Instruct', price: '0.1', category: 'Pro' },
        { id: 'Pro/THUDM/chatglm3-6b', name: 'Pro/chatglm3-6b', price: '0.35', category: 'Pro' },
        { id: 'Pro/THUDM/glm-4-9b-chat', name: 'Pro/glm-4-9b-chat', price: '0.5', category: 'Pro' }
      ]
    }
  },
  computed: {
    // 按类别分组的模型列表
    modelsByCategory() {
      const result = {}
      this.modelList.forEach(model => {
        if (!result[model.category]) {
          result[model.category] = []
        }
        result[model.category].push(model)
      })
      return result
    },
    isVVMModel() {
      // 判断是否为VVM模型，这里简单根据模型ID判断
      return this.selectedModel.includes('DeepSeek') && !this.selectedModel.includes('V')
    },
    isVLMModel() {
      // 判断是否为VLM模型
      return this.selectedModel.includes('V')
    }
  },
  watch: {
    messages: {
      handler() {
        this.scrollToBottom()
      },
      deep: true
    }
  },
  mounted() {
    // 初始化时确保滚动条在底部
    this.scrollToBottom()

    // 添加窗口调整大小的监听器
    window.addEventListener('resize', this.scrollToBottom)

    // 添加消息容器滚动监听器
    if (this.$refs.messagesBox) {
      this.$refs.messagesBox.addEventListener('scroll', this.handleScroll)
    }
  },
  updated() {
    // 组件更新后再次滚动到底部
    this.scrollToBottom()
  },
  beforeDestroy() {
    // 移除事件监听器
    window.removeEventListener('resize', this.scrollToBottom)

    if (this.$refs.messagesBox) {
      this.$refs.messagesBox.removeEventListener('scroll', this.handleScroll)
    }

    // 关闭可能存在的流式连接
    this.stopStream()
  },
  methods: {
    // 处理回车键按下事件
    handleEnterPress(event) {
      // 如果按下的是Shift+Enter，不做处理（允许换行）
      if (event.shiftKey) return

      // 阻止默认行为（插入换行符）
      event.preventDefault()

      // 发送消息
      this.sendPrompt()
    },

    handleScroll() {
      // 可以在这里添加检测用户是否主动滚动的逻辑
      // 如果需要的话
    },
    scrollToBottom() {
      this.$nextTick(() => {
        if (this.$refs.messagesBox) {
          // 使用setTimeout确保DOM已完全更新
          setTimeout(() => {
            const messagesBox = this.$refs.messagesBox
            messagesBox.scrollTop = messagesBox.scrollHeight
          }, 0)
        }
      })
    },
    sendPrompt() {
      if (!this.input.trim() || this.isStreaming) return
      const prompt = this.input.trim()
      this.input = ''

      // 加入用户消息
      this.messages.push({ role: 'user', content: prompt })

      // 预先加入空的 AI 消息
      const aiMsg = { role: 'assistant', content: '' }
      this.messages.push(aiMsg)

      // 立即滚动到底部
      this.scrollToBottom()

      // 创建流式连接
      this.isStreaming = true
      this.lastEventData = null
      const token = getToken()

      // 构建请求参数
      const queryParams = new URLSearchParams({
        prompt: prompt,
        token: token,
        model: this.selectedModel,
        max_tokens: this.params.maxTokens,
        temperature: this.params.temperature,
        top_p: this.params.topP,
        top_k: this.params.topK,
        frequency_penalty: this.params.frequencyPenalty
      })

      // 如果是VVM模型，添加thinking_budget参数
      if (this.isVVMModel) {
        queryParams.append('thinking_budget', this.params.thinkingBudget)
      }

      const url = '/api/stream/sse?' + queryParams.toString()
      this.source = new EventSource(url)

      this.source.onmessage = (event) => {
        try {
          // 保存最后一次事件数据
          this.lastEventData = event.data

          // 检查是否是结束标记 [DONE]
          if (event.data === '[DONE]') {
            console.log('流式传输正常结束')
            this.stopStream()
            return
          }

          if (!event.data) return // 如果数据为空，直接返回

          const chunk = JSON.parse(event.data)
          // 检查是否有有效内容
          if (chunk && chunk.choices && chunk.choices[0] && chunk.choices[0].delta) {
            const content = chunk.choices[0].delta.content
            // 只有当content不是null或undefined时才添加
            if (content !== null && content !== undefined) {
              // 处理特殊字符，确保HTML安全
              const safeContent = this.escapeHtml(content)
              // 替换换行符为<br>标签
              const formattedContent = safeContent.replace(/\n/g, '<br>')
              aiMsg.content += formattedContent
              // 每次收到新内容都滚动到底部
              this.scrollToBottom()
            }
          }
        } catch (err) {
          console.error('解析错误', err)
        }
      }

      this.source.onerror = (err) => {
        console.error('SSE 错误', err)

        // 检查是否是正常结束
        if (this.lastEventData === '[DONE]') {
          console.log('流式传输正常结束')
          this.stopStream()
          return
        }

        this.stopStream()

        // 添加错误信息到AI消息中
        aiMsg.error = '服务器错误，请稍后再试'

        // 显示错误通知
        this.$notify.error({
          title: '连接错误',
          message: '服务器返回错误 (403)，请稍后再试',
          duration: 3000
        })

        // 滚动到底部以显示错误消息
        this.scrollToBottom()
      }

      // 确保滚动到底部
      this.scrollToBottom()
    },

    // 转义HTML特殊字符，防止XSS攻击
    escapeHtml(text) {
      if (!text) return ''
      return text
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#039;')
    },

    stopStream() {
      if (this.source) {
        this.source.close()
        this.source = null
      }
      this.isStreaming = false
    },

    clearChat() {
      this.$confirm('确定要清空所有对话记录吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.messages = []
        this.$message({
          type: 'success',
          message: '对话已清空'
        })
      }).catch(() => {})
    },

    getSelectedModelName() {
      const model = this.modelList.find(m => m.id === this.selectedModel)
      return model ? model.name : '未选择模型'
    },

    getModelPrice() {
      const model = this.modelList.find(m => m.id === this.selectedModel)
      return model ? (model.price === '免费' ? '免费' : '¥' + model.price + '/M Tokens') : ''
    },

    getModelPriceType() {
      const model = this.modelList.find(m => m.id === this.selectedModel)
      return model && model.price === '免费' ? 'success' : 'warning'
    }
  }
}
</script>

<style scoped>
.app-container {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
}

.chat-page {
  width: 100%;
  height: calc(100vh - 50px); /* 适应AppMain的高度 */
  padding: 20px;
  box-sizing: border-box;
  overflow: hidden;
}

.settings-panel {
  height: 100%;
  overflow-y: auto;
}

.settings-header {
  font-weight: bold;
  font-size: 16px;
}

.setting-section {
  margin-bottom: 20px;
}

.section-title {
  font-size: 14px;
  color: #606266;
  margin-bottom: 10px;
  border-bottom: 1px solid #EBEEF5;
  padding-bottom: 5px;
}

.param-item {
  margin-bottom: 15px;
}

.param-label {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 5px;
  font-size: 13px;
  color: #606266;
}

.param-value {
  text-align: center;
  color: #409EFF;
  font-size: 12px;
}

.chat-container {
  height: 100%;
  display: flex;
  flex-direction: column;
}

.chat-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-bottom: 10px;
  border-bottom: 1px solid #EBEEF5;
  margin-bottom: 10px;
  flex-shrink: 0; /* 防止头部被压缩 */
}

.chat-body {
  display: flex;
  flex-direction: column;
  height: calc(100% - 50px); /* 减去头部的高度 */
}

.model-info {
  display: flex;
  align-items: center;
}

.model-name {
  font-weight: bold;
  margin-right: 10px;
}

.messages {
  flex: 1;
  overflow-y: auto;
  padding: 10px;
  background: #f5f7fa;
  border-radius: 4px;
  margin-bottom: 10px;
}

.empty-chat {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  color: #909399;
}

.empty-chat i {
  font-size: 48px;
  margin-bottom: 10px;
}

.message-item {
  display: flex;
  margin-bottom: 20px;
}

.message-avatar {
  margin-right: 10px;
  flex-shrink: 0; /* 防止头像被压缩 */
}

.message-content {
  flex: 1;
  max-width: calc(100% - 60px); /* 确保内容不会太宽 */
}

.message-role {
  font-size: 12px;
  color: #909399;
  margin-bottom: 4px;
}

.message-item .content {
  padding: 10px 15px;
  border-radius: 8px;
  background: white;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
  word-break: break-word;
  white-space: pre-wrap;
  overflow-wrap: break-word; /* 确保长单词也能换行 */
}

.error-message {
  margin-top: 5px;
  color: #f56c6c;
  font-size: 12px;
  padding: 5px 10px;
  background-color: #fef0f0;
  border-radius: 4px;
}

.message-item.user .content {
  background: #ecf5ff;
}

.message-item.assistant .content {
  background: white;
}

.input-area {
  flex-shrink: 0; /* 防止输入区域被压缩 */
  background-color: white;
  padding-top: 10px;
  border-top: 1px solid #EBEEF5;
  margin-top: auto; /* 将输入区域推到底部 */
}

.input-box {
  margin-bottom: 10px;
}

.buttons {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}

/* 确保el-row和el-col占满高度 */
.el-row, .el-col {
  height: 100%;
}

/* 确保卡片组件占满高度 */
.el-card {
  height: 100%;
  display: flex;
  flex-direction: column;
}

.el-card__body {
  flex: 1;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
</style>
